﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Unity;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Windows.UI.Infrastructure.Interfaces;
using StockholmsStad.Windows.UI.Modules.Maintenance.BusinessEntities;
using StockholmsStad.Windows.UI.Modules.Maintenance.Interfaces;
using StockholmsStad.Windows.UI.Facade;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using System.Windows;
using StockholmsStad.Windows.UI.Modules.Maintenance.Models;

namespace StockholmsStad.Windows.UI.Modules.Maintenance.Services
{
    public class MaintenanceService : IMaintenanceService
    {
        private const string STARTTIME = "14:00";
        private const string ENDTIME = "16:00";
        private const int INTERVAL = 5;
        private const string FIRST_BREAK = "14:55";
        private const string SECOND_BREAK = "15:05";

        private ObservableCollection<DepersonateCouple> depersonateCouples;

        private IApplicationService applicationService;
        [Dependency]
        public IApplicationService ApplicationService
        {
            set { applicationService = value; }
        }

        #region IMaintenanceService Members

        public GenerateTimePresentationModel RetrieveSchedule()
        {
            var timeBlocks = GetTimeBlocks();
            return ConvertTimeBlockToPresentationModel(timeBlocks);
        }

        public void GetMinisters(MinisterTimeBlockPresentationModel model, DateTime date)
        {
            model.Saved += TimeBlock_Saved;
            model.Cancelled += TimeBlock_Cancelled;
            model.Ministers = GetMinisters(date);
        }

        #endregion

        #region Private helper methods
        private void ScheduleGenerated(object sender, DataEventArgs<GenerateTimePresentationModel> e)
        {
            if (e != null && e.Value != null)
            {
                GenerateTimeBlocks(e);
           }
        }

        private GenerateTimePresentationModel ConvertTimeBlockToPresentationModel(IEnumerable<MinisterTimeBlock> timeBlocks)
        {
            var model = new GenerateTimePresentationModel();
            model.Generated += ScheduleGenerated;

            if (model.ScheduledMonths == null)
                model.ScheduledMonths = new ScheduledMonths();

            try
            {

                ScheduledMonth scheduledMonth = null;
                var lastEnteredDay = 0;
                var lastEnteredMonth = 0;
                foreach (var timeBlock in timeBlocks)
                {
                    var timeBlockPresentationModel = new MinisterTimeBlockPresentationModel
                                                         {
                                                             MinisterTimeBlocks =
                                                                 new ObservableCollection<MinisterTimeBlock>(),
                                                             Date = timeBlock.StartDateTime
                                                         };

                    timeBlockPresentationModel.Cancelled += TimeBlock_Cancelled;
                    timeBlockPresentationModel.Saved += TimeBlock_Saved;

                    if (scheduledMonth != null && timeBlock.StartDateTime.Day == lastEnteredDay &&
                        timeBlock.StartDateTime.Month == lastEnteredMonth)
                    {
                        scheduledMonth.EndTime = timeBlock.StartDateTime;
                        scheduledMonth.TimeBlockPresentationModel.MinisterTimeBlocks.Add(timeBlock);
                    }
                    else
                    {
                        // Skapa ny månad
                        scheduledMonth = new ScheduledMonth
                                             {
                                                 Date = timeBlock.StartDateTime,
                                                 StartTime = timeBlock.StartDateTime,
                                                 EndTime = timeBlock.StartDateTime,
                                             };

                        if (scheduledMonth.TimeBlockPresentationModel == null)
                            scheduledMonth.TimeBlockPresentationModel = new MinisterTimeBlockPresentationModel();

                        if (scheduledMonth.TimeBlockPresentationModel.MinisterTimeBlocks == null)
                            scheduledMonth.TimeBlockPresentationModel.MinisterTimeBlocks =
                                new ObservableCollection<MinisterTimeBlock>();

                        scheduledMonth.TimeBlockPresentationModel.MinisterTimeBlocks.Add(timeBlock);

                        if (model.ScheduledMonths.Months == null)
                            model.ScheduledMonths.Months = new ObservableCollection<ScheduledMonth>();

                        if(scheduledMonth.TimeBlockPresentationModel.Date.Equals(DateTime.MinValue))
                            scheduledMonth.TimeBlockPresentationModel.Date = scheduledMonth.Date;

                        model.ScheduledMonths.Months.Add(scheduledMonth);
                    }

                    lastEnteredDay = timeBlock.StartDateTime.Day;
                    lastEnteredMonth = timeBlock.StartDateTime.Month;
                }

            }
            catch (Exception e)
            {
                // Do something
                Console.WriteLine(e.Message);
            }

            return model;
        }

        private void TimeBlock_Saved(object sender, DataEventArgs<MinisterTimeBlockPresentationModel> e)
        {
            if (e != null && e.Value != null)
            {
                var model = e.Value;
                var user = applicationService.GetCurrentUser();
                var facade = new MaintenanceFacade(user);

                var timeBlocks = CreateTimeBlocksFromPresentationModel(model);

                var list = facade.UpdateTimeBlocks(timeBlocks);

                foreach (var tb in model.MinisterTimeBlocks)
                {
                    foreach (var updated in list)
                    {
                        // Uppdatera endast id om inte det finns satt, annars är modellen korrekt som den är
                        if (!tb.TimeBlockId.HasValue && tb.StartDateTime == updated.StartDateTime)
                        {
                            tb.TimeBlockId = updated.TimeBlockId;
                        }
                    }
                }
            }
        }

        private void TimeBlock_Cancelled(object sender, DataEventArgs<MinisterTimeBlockPresentationModel> e)
        {
            if (e != null && e.Value != null)
            {
                var model = e.Value;
                var timeBlocks = GetTimeBlocks(e.Value.Date);

                if (timeBlocks != null && timeBlocks.Count > 0)
                    model.MinisterTimeBlocks = timeBlocks;
                else
                {
                    // Detta är nytillagda tider som inte finns i databasen, måste alltså generera om de från scratch
                    var startDate = model.MinisterTimeBlocks[0].StartDateTime;
                    var lastIndex = model.MinisterTimeBlocks.Count - 1;
                    var interval = model.MinisterTimeBlocks[1].StartDateTime.Minute -
                                   model.MinisterTimeBlocks[0].StartDateTime.Minute;
                    TimeSpan timespan = model.MinisterTimeBlocks[lastIndex].StartDateTime.Subtract(model.MinisterTimeBlocks[0].StartDateTime);
                    model.MinisterTimeBlocks.Clear();
                    GenerateStartTimes(interval, startDate, model, timespan);
                }
            }
        }

        private ObservableCollection<MinisterTimeBlock> GetTimeBlocks(DateTime date)
        {
            var user = applicationService.GetCurrentUser();
            var facade = new MaintenanceFacade(user);
            return facade.GetTimeBlocks(date);

        }

        private List<MinisterTimeBlock> GetTimeBlocks()
        {
            var user = applicationService.GetCurrentUser();
            var facade = new MaintenanceFacade(user);
            return facade.GetTimeBlocks();
        }

        private void GenerateTimeBlocks(DataEventArgs<GenerateTimePresentationModel> e)
        {
            GenerateTimePresentationModel schedule = e.Value;

            if (string.IsNullOrEmpty(schedule.StartTime))
                schedule.StartTime = STARTTIME;

            if (string.IsNullOrEmpty(schedule.EndTime))
                schedule.EndTime = ENDTIME;

            if (schedule.Interval == 0)
                schedule.Interval = INTERVAL;

            var separator = new[] { ":" };
            string[] timeToStart = schedule.StartTime.Split(separator, StringSplitOptions.None);
            string[] timeToEnd = schedule.EndTime.Split(separator, StringSplitOptions.None);

            var startDate = schedule.StartDate;
            var endDate = schedule.EndDate;

            var startStartDateTime = new DateTime(startDate.Value.Year, startDate.Value.Month, startDate.Value.Day,
                                                  Convert.ToInt32(timeToStart[0]), Convert.ToInt32(timeToStart[1]),
                                                  0);
            var startEndDateTime = new DateTime(startDate.Value.Year, startDate.Value.Month, startDate.Value.Day,
                                                Convert.ToInt32(timeToEnd[0]), Convert.ToInt32(timeToEnd[1]), 0);

            TimeSpan timespanStartDay = startEndDateTime.Subtract(startStartDateTime);

            var iterateDate = startStartDateTime;

            if (schedule.IncludeAnotherDay)
                endDate = startEndDateTime;

            while (iterateDate <= endDate)
            {
                if ((iterateDate.DayOfWeek == DayOfWeek.Saturday) || schedule.IncludeAnotherDay)
                {
                    if (!TimeBlockAlreadyExists(schedule, iterateDate))
                    {

                        var timeBlockPresentationModel = new MinisterTimeBlockPresentationModel
                                                             {
                                                                 Date = iterateDate,
                                                             };

                        timeBlockPresentationModel.Cancelled += TimeBlock_Cancelled;
                        timeBlockPresentationModel.Saved += TimeBlock_Saved;

                        GenerateStartTimes(schedule.Interval, iterateDate, timeBlockPresentationModel, timespanStartDay);

                        var scheduledMonth = new ScheduledMonth
                                                 {
                                                     Date = iterateDate,
                                                     StartTime =
                                                         new DateTime(iterateDate.Year, iterateDate.Month,
                                                                      iterateDate.Day,
                                                                      Convert.ToInt32(timeToStart[0]),
                                                                      Convert.ToInt32(timeToStart[1]), 0),
                                                     EndTime =
                                                         new DateTime(iterateDate.Year, iterateDate.Month,
                                                                      iterateDate.Day,
                                                                      Convert.ToInt32(timeToEnd[0]),
                                                                      Convert.ToInt32(timeToEnd[1]), 0),
                                                     TimeBlockPresentationModel = timeBlockPresentationModel,
                                                 };


                        if (schedule.ScheduledMonths == null)
                            schedule.ScheduledMonths = new ScheduledMonths();

                        if(schedule.ScheduledMonths.Months == null)
                            schedule.ScheduledMonths.Months = new ObservableCollection<ScheduledMonth>();

                        schedule.ScheduledMonths.Months.Add(scheduledMonth);
                    }
                    else
                    {
                        // Tidsblocket finns redan, kan inte lägga till detta
                        var message = string.Format("Det finns redan ett tidsblock för datum {0}. Detta tidsblock läggs inte till i listan. ",
                                                    iterateDate.ToString("yyyy-MM-dd"));
                        MessageBox.Show(message, "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                    }
                }

                iterateDate = iterateDate.AddDays(1);
            }
        }

        private static void GenerateStartTimes(int interval, DateTime iterateDate, 
            MinisterTimeBlockPresentationModel timeBlockPresentationModel, TimeSpan timeSpan)
        {
            for (int minuteIndex = 0; minuteIndex <= timeSpan.Hours * 60 + timeSpan.Minutes; minuteIndex++)
            {

                // Generera tider med angivet intervall
                if (minuteIndex%interval == 0)
                {
                    var tb = new MinisterTimeBlock();
                    var newTime = iterateDate.AddMinutes(minuteIndex);
                    tb.StartDateTime = newTime;
                    tb.Interval = interval;

                    if (tb.StartDateTime.ToString("HH:mm").Equals(FIRST_BREAK) ||
                        tb.StartDateTime.ToString("HH:mm").Equals(SECOND_BREAK))
                        tb.TimeBlockStatus = TimeBlockStatus.Break;
                    else
                        tb.TimeBlockStatus = TimeBlockStatus.Bookable;

                    if (timeBlockPresentationModel.MinisterTimeBlocks == null)
                        timeBlockPresentationModel.MinisterTimeBlocks = new ObservableCollection<MinisterTimeBlock>();

                    timeBlockPresentationModel.MinisterTimeBlocks.Add(tb);
                }
            }
        }

        private ObservableCollection<Minister> GetMinisters(DateTime date)
        {
            var user = applicationService.GetCurrentUser();
            var facade = new MinisterFacade(user);
            return facade.GetMinistersForDate(date);
        }

        private static bool TimeBlockAlreadyExists(GenerateTimePresentationModel model, DateTime searchDate)
        {
            if (model.ScheduledMonths == null || model.ScheduledMonths.Months == null)
                return false;

            foreach (var month in model.ScheduledMonths.Months)
            {
                if(month.Date.ToString("yyyy-MM-dd").Equals(searchDate.ToString("yyyy-MM-dd")))
                    return true;
            }

            return false;
        }

        private static List<MinisterTimeBlock> CreateTimeBlocksFromPresentationModel(MinisterTimeBlockPresentationModel model)
        {
            var ministerTimeBlocks = new List<MinisterTimeBlock>();

            foreach (var timeBlock in model.MinisterTimeBlocks)
            {
                var tb = new MinisterTimeBlock
                {
                    Interval = timeBlock.Interval,
                    StartDateTime = timeBlock.StartDateTime,
                    TimeBlockStatus = timeBlock.TimeBlockStatus,
                };

                if (timeBlock.Minister != null)
                    tb.Minister = new Minister { MinisterId = timeBlock.Minister.MinisterId };

                if (timeBlock.TempMinister != null)
                    tb.TempMinister = new Minister { MinisterId = timeBlock.TempMinister.MinisterId };

                if (timeBlock.TimeBlockId.HasValue)
                    tb.TimeBlockId = timeBlock.TimeBlockId.Value;

                ministerTimeBlocks.Add(tb);
            }

            return ministerTimeBlocks;
        }

        private ObservableCollection<DepersonateCouple> CreateDepersonateCouples(IEnumerable<Couple> couples)
        {
            depersonateCouples = new ObservableCollection<DepersonateCouple>();
            foreach (var couple in couples)
            {
                var model = new DepersonateCouple
                                {
                                    ContactPerson = couple.ContactPerson,
                                    CoupleId = couple.CoupleId,
                                    Depersonate = false,
                                    Person1 = couple.Person1,
                                    Person2 = couple.Person2,
                                };
                depersonateCouples.Add(model);
            }
            return depersonateCouples;
        }

        private static Couple CreateCoupleFromModel(DepersonateCouple model)
        {
            var couple = new Couple
                             {
                                ContactPerson = model.ContactPerson,
                                CoupleId = model.CoupleId,
                                Person1 = model.Person1,
                                Person2 = model.Person2,
                             };
            return couple;
        }
        #endregion

        public ObservableCollection<DepersonateCouple> GetCouplesForDepersonation(DateTime fromDate, DateTime toDate)
        {
            var user = applicationService.GetCurrentUser();
            var facade = new MaintenanceFacade(user);
            var couples = facade.GetCouplesForDepersonation(fromDate, toDate);
            return CreateDepersonateCouples(couples);
        }

        public int DepersonateCouples(IList couples)
        {
            var list = new List<Couple>();

            foreach (DepersonateCouple model in couples)
            {
                list.Add(CreateCoupleFromModel(model));
            }

            var user = applicationService.GetCurrentUser();
            var facade = new MaintenanceFacade(user);
            return facade.DepersonateCouples(list);
        }

    }
}